package org.beynet.jsonbinding.parser.types.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.beynet.jsonbinding.parser.JSONParsingException;
import org.beynet.jsonbinding.parser.JSONParsingExceptionUnexpectedChar;
import org.beynet.jsonbinding.parser.types.JSONMember;
import org.beynet.jsonbinding.parser.types.JSONObject;

public class JSONObjectImpl extends JSONAbstractObject implements JSONObject {
    JSONObjectImpl(String encoding,InputStream is) throws JSONParsingException, IOException {
        super(encoding);
        this.state = Object_State.EXPECT_STRING_OR_END;
        loopContent(is);
    }
    
    /**
     * JSONObject construction main loop : from state INIT to state END
     * @param is
     * @throws IOException
     * @throws JSONParsingException
     */
    private void loopContent(InputStream is) throws IOException, JSONParsingException {

        while (state != Object_State.END) {
       
            if (state == Object_State.EXPECT_STRING_OR_END) {
                int f = consumeSpaces(is);
                if (f=='}') {
                    state = Object_State.END;
                }
                else if (f=='"'){
                    lastString = readStringContent(is);
                    state = Object_State.EXPECT_COLUMN;
                }
                else {
                    throw new JSONParsingExceptionUnexpectedChar((char) f, "\" or }");
                }
            }
            if (state == Object_State.EXPECT_STRING) {
                lastString = readString(is);
                state = Object_State.EXPECT_COLUMN;
            }
            if (state == Object_State.EXPECT_COLUMN) {
                readColumn(is);
                state = Object_State.EXPECT_VALUE;
            }
            if (state == Object_State.EXPECT_VALUE) {
                Object value = readValue(is);
                members.add(new JSONMember(lastString, value));
                state = Object_State.EXPECT_END_OR_NEXT;
            }
            if (state == Object_State.EXPECT_END_OR_NEXT) {
                int res;
                if (next!=-1) {
                    res = next;
                    next=-1;
                }
                else res = consumeSpaces(is);
                if (res == ',') {
                    state = Object_State.EXPECT_STRING;
                } else if (res == '}') {
                    state = Object_State.END;
                } else {
                    throw new JSONParsingExceptionUnexpectedChar(res);
                }
            }
        }
    }


    protected void readToBeginOfString(InputStream is) throws IOException, JSONParsingException {
        int r = 0;
        while (true) {
            r = is.read();
            if (r == '"')
                return;
            if (!(r == ' ' || r == '\t' || r == '\n' || r == '\r')) {
                throw new JSONParsingExceptionUnexpectedChar((char) r, "\"");
            }
        }
    }

    /**
     * read an expected string
     * @param is
     * @throws IOException
     * @throws JSONParsingException
     */
    protected String readString(InputStream is) throws IOException, JSONParsingException {
        readToBeginOfString(is);
        return (readStringContent(is));
    }

    

    /**
     * read expected char ':'
     * 
     * @param is
     * @throws IOException
     * @throws JSONParsingException
     */
    protected void readColumn(InputStream is) throws IOException, JSONParsingException {
        int r = 0;
        while (true) {
            r = is.read();
            if (r == ':') {
                return;
            }
            if (!(r == ' ' || r == '\t' || r == '\n' || r == '\r')) {
                throw new JSONParsingExceptionUnexpectedChar((char) r, ":");
            }
        }
    }


    @Override
    public List<JSONMember> getMembers() {
        return members;
    }

    private List<JSONMember> members = new ArrayList<>();
    private String lastString = null;

}
